package com.twotwoshop.service.goods.service.Impl;

import com.twotwoshop.commom.redis.RedisConstants;
import com.twotwoshop.domin.goods.pojo.Product;
import com.twotwoshop.domin.goods.pojo.ProductComment;
import com.twotwoshop.domin.goods.pojo.ProductFavorite;
import com.twotwoshop.service.goods.mapper.ProductFavoriteMapper;
import com.twotwoshop.service.goods.mapper.ProductMapper;
import com.twotwoshop.service.goods.service.ProductHotService;
import com.twotwoshop.service.goods.service.ProductService;
import com.twotwoshop.util.thread.AppThreadLocalUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
@RequiredArgsConstructor
public class ProductHotServiceImpl implements ProductHotService {


    private final StringRedisTemplate stringRedisTemplate;

    private final ProductMapper productMapper;

    private final ProductFavoriteMapper productFavoriteMapper;

    private final RedissonClient redissonClient;

    //线程池
    @Autowired
    private ExecutorService executorService;

    //每次批量导入Redis数据的数量
    private static final int PAGE_SIZE = 500;


    /**
     * 同步商品浏览量进数据库
     */
    @Override
    public void computeHotGoods() {

        //获取浏览量增加的分布式锁
        RLock lock = redissonClient.getLock(RedisConstants.GOODS_HOT_LOCK_KEY);

        //默认没有获取锁
        boolean isLocked = false;



        try {

            // 尝试获取锁，等待时间为 10 秒，锁过期时间为 30 秒
            isLocked = lock.tryLock(10, 30, TimeUnit.SECONDS);

            //获取到锁了在进行同步逻辑
            if (isLocked) {

                //异步执行同步数据库
                processHotGoods();

            }else {
                log.info("未能获取锁，跳过本次执行");
            }

        } catch (InterruptedException e) {
            log.error("批量更新浏览数据失败", e);

            Thread.currentThread().interrupt();

            throw new RuntimeException("批量更新失败", e);
        } catch (Exception e) {
            log.error("批量更新视图数据失败", e);
            throw new RuntimeException("批量更新失败", e);
        }finally {

            //必须释放锁
            if (isLocked) {
                lock.unlock();
            }
        }

    }


    /**
     * 同步商品收藏信息
     */
    @Override
    public void computeFavouriteGoods() {

        //获取商品收藏增加的分布式锁
        RLock lock = redissonClient.getLock(RedisConstants.GOODS_FAVOURITE_LOCK_KEY);

        //默认没有获取锁
        boolean isLocked = false;



        try {

            // 尝试获取锁，等待时间为 10 秒，锁过期时间为 30 秒
            isLocked = lock.tryLock(10, 30, TimeUnit.SECONDS);

            //获取到锁了在进行同步逻辑
            if (isLocked) {

                //异步执行同步数据库
                processFavouriteGoods();

            }else {
                log.info("未能获取锁，跳过本次执行");
            }

        } catch (InterruptedException e) {
            log.error("批量更新收藏数据失败", e);

            Thread.currentThread().interrupt();

            throw new RuntimeException("批量更新失败", e);
        } catch (Exception e) {
            log.error("批量更新收藏数据失败", e);
            throw new RuntimeException("批量更新失败", e);
        }finally {

            //必须释放锁
            if (isLocked) {
                lock.unlock();
            }
        }

    }

    //同步商品收藏
    private void processFavouriteGoods() {

        Cursor<byte[]> cursor = stringRedisTemplate.getConnectionFactory().getConnection().scan(
                ScanOptions.scanOptions().match(RedisConstants.GOODS_FAVOURITE_HISTORY_KEY+"*").count(1000).build()
        );

        //通过scan命令获取到全部的key 然后挨个处理
        while (cursor.hasNext()) {
            String key = new String(cursor.next());
            // 处理每个键

            //获取每个用户的全部收藏信息
            Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet().rangeWithScores(
                    key,
                    0,
                    -1
            );

            if (typedTuples == null || typedTuples.isEmpty()) {
                throw new RuntimeException("Redis中商品收藏同步失败");
            }

            List<ProductFavorite> favoriteList = new ArrayList<>();

            int lastColonIndex = key.lastIndexOf(":");
            String userId = key.substring(lastColonIndex + 1);


            // 2. 转换数据格式
            for (ZSetOperations.TypedTuple<String> item : typedTuples) {

                String productId = item.getValue();
                Double score = item.getScore();

                //转化时间为localDataTime
                LocalDateTime dateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(score.longValue()), ZoneId.systemDefault());

                if (productId != null && score != null) {
                    ProductFavorite productFavorite = new ProductFavorite();

                    //设置单个商品浏览量
                    productFavorite.setProductId(Long.parseLong(productId));
                    productFavorite.setUserId(Long.valueOf(userId));
                    productFavorite.setCreatedTime(dateTime);

                    //将商品添加到集合中
                    favoriteList.add(productFavorite);
                }
            }


            //批量更新收藏量
            productFavoriteMapper.insertBatvh(favoriteList);

            log.info("批量更新用户收藏信息成功");
        }



    }

    //保存浏览数据
    private void processHotGoods() throws InterruptedException {

        // 1. 获取所有商品ID及其浏览量
        Set<ZSetOperations.TypedTuple<String>> viewsWithScores =
                stringRedisTemplate.opsForZSet().rangeWithScores(
                        RedisConstants.GOODS_VIEWS_KEY,
                        0,
                        -1
                );

        if (viewsWithScores == null || viewsWithScores.isEmpty()) {
            throw new RuntimeException("Redis中商品浏览量同步失败");
        }

        List<Product> updateList = new ArrayList<>();

        // 2. 转换数据格式
        for (ZSetOperations.TypedTuple<String> item : viewsWithScores) {
            String productId = item.getValue();
            Double score = item.getScore();
            if (productId != null && score != null) {
                Product product = new Product();

                //设置单个商品浏览量
                product.setId(Long.parseLong(productId));
                product.setViews(score.intValue());

                //将商品添加到集合中
                updateList.add(product);
            }
        }

        //1.获取数据总条数
        int count = updateList.size();

        //2.总页数
        int totalPageSize = count % PAGE_SIZE == 0 ? count / PAGE_SIZE : count / PAGE_SIZE + 1;

        //3.有多少页就添加多少个倒计时
        CountDownLatch countDownLatch = new CountDownLatch(totalPageSize);

        for (int i = 0; i < totalPageSize; i++) {
            // 计算每页的起始和结束索引
            int startIndex = i * PAGE_SIZE;
            int endIndex = Math.min((i + 1) * PAGE_SIZE, count);

            // 获取当前页的数据
            List<Product> batch = updateList.subList(startIndex, endIndex);

            // 提交到线程池执行
            executorService.submit(() -> {
                try {

                    // 执行批量更新浏览量
                    productMapper.batchUpdateViewCount(batch);

                    log.info("批量更新成功，当前批次数量：{}", batch.size());
                } catch (Exception e) {
                    log.error("批量更新失败，批次大小：{}", batch.size(), e);
                } finally {
                    // 完成一页，计数器减1
                    countDownLatch.countDown();
                }
            });
        }

        if (!countDownLatch.await(30, TimeUnit.SECONDS)) {
            log.warn("部分更新任务未在规定时间内完成");
        }
    }


        /*      //1.查询redis中的商品浏览信息
                // 1. 获取所有商品ID及其浏览量
                Set<ZSetOperations.TypedTuple<String>> viewsWithScores =
                        stringRedisTemplate.opsForZSet().rangeWithScores(
                                RedisConstants.GOODS_VIEWS_KEY,
                                0,
                                -1
                        );

                //判空
                if (viewsWithScores == null || viewsWithScores.isEmpty()) {

                    throw new RuntimeException("Redis中商品浏览量同步失败");
                }


                // 2. 转换数据格式
                List<Product> updateList = new ArrayList<>();
                for (ZSetOperations.TypedTuple<String> item : viewsWithScores) {
                    String productId = item.getValue();
                    Double score = item.getScore();
                    if (productId != null && score != null) {

                        Product product = new Product();

                        //设置单个商品浏览量
                        product.setId(Long.parseLong(productId));
                        product.setViews(score.intValue());

                        //全部存到集合中
                        updateList.add(product);
                    }
                }


                //1.获取数据总条数
                int count = updateList.size();

                //2.总页数
                int totalPageSize = count % PAGE_SIZE == 0 ? count / PAGE_SIZE : count / PAGE_SIZE + 1;


                //3.一共有多少页 就创建多少个CountDownLatch的计数
                CountDownLatch countDownLatch = new CountDownLatch(totalPageSize);

                for (int i = 0; i < totalPageSize; i++) {

                    // 计算每页的起始和结束索引
                    int startIndex = i * PAGE_SIZE;
                    int endIndex = Math.min((i + 1) * PAGE_SIZE, count);

                    // 获取当前页的数据
                    List<Product> batch = updateList.subList(startIndex, endIndex);

                    // 提交到线程池执行
                    executorService.submit(() -> {
                        try {

                            // 执行批量更新
                            productMapper.batchUpdateViewCount(batch);

                            log.info("批量更新成功，当前批次数量：{}", batch.size());
                        } catch (Exception e) {
                            log.error("批量更新失败，批次大小：{}", batch.size(), e);
                        } finally {
                            // 完成一页，计数器减1
                            countDownLatch.countDown();
                        }
                    });


                }

                // 6.等待所有任务完成
                if (!countDownLatch.await(30, TimeUnit.SECONDS)) {
                    log.warn("部分更新任务未在规定时间内完成");
                }*/
}
